import path from 'path'
import { isSubPath } from '@/utils/path'
import { ServerConnectAction } from '@/server/action'
import type { ActionsNext } from '@/utils/action'
import type { ServerConnect } from '@/server/connect'
import type { FileStructureDiffResult } from '@/utils/fileStructure'

export class FileDiffAction extends ServerConnectAction {
  constructor(connect: ServerConnect) {
    super('fileDiff', connect)

    this.addAction('sendHashMap', this.sendHashMap_handler, true)
    this.addAsyncAction('receiveDiffResult', this.receiveDiffResult_handler)
    this.addAction('hasChanged', this.hasChanged_handler)
    this.addAction('notChanged', this.notChanged_handler)
    this.addAsyncAction('needToSend', this.needToSend_handler)
    this.addAsyncAction('noNeedToSend', this.noNeedToSend_handler)

    this.actions.addBranchList([
      ['sendHashMap', 'receiveDiffResult'],
      ['receiveDiffResult', 'hasChanged'],
      ['receiveDiffResult', 'notChanged'],
      ['hasChanged', 'needToSend'],
      ['hasChanged', 'noNeedToSend']
    ])
  }

  async sendHashMap_handler(next: ActionsNext) {
    if (!this.connect.project) {
      this.logger.error('project is not exist')
      return this.connect.end()
    }
    let cachePath: string | null = this.connect.project.config.cache.root
    if (!isSubPath(this.connect.project.config.path, cachePath)) {
      cachePath = null
    } else {
      path.relative(this.connect.project.config.path, cachePath)
    }

    const hashMap = await this.connect.project.getHashMap()
    this.connect.project?.config.cache.root
    this.connect.sendJson({ hashMap, cachePath })
    this.logger.info('send hashMap')
    next()
  }

  async receiveDiffResult_handler() {
    const p = await this.connect.packetOnceAsync()
    const diffResult = p.readBodyAsJson<FileStructureDiffResult>()
    if (!diffResult) {
      throw new Error('not data')
    }
    this.connect.store.set('fileStructureDiffResult', diffResult)
    this.logger.info('received diff result')
    return diffResult.hasChanged ? 'hasChanged' : 'notChanged'
  }

  hasChanged_handler(next: ActionsNext) {
    const diffResult = this.connect.store.get<FileStructureDiffResult>('fileStructureDiffResult')
    this.logger.info('has changed', {
      added: diffResult.added_count,
      changed: diffResult.changed_count,
      removed: diffResult.removed_count,
      sended: diffResult.sended_count,
      sended_duplicate: diffResult.sended_duplicate_count,
      sended_empty_count: diffResult.sended_empty_count
    })

    next(diffResult.hasSended ? 'needToSend' : 'noNeedToSend')
  }

  notChanged_handler() {
    this.logger.info('not changed')
    this.connect.end()
  }

  async needToSend_handler() {
    this.logger.info('need to send')
    this.nextName = 'fileReceive'
  }

  async noNeedToSend_handler() {
    this.logger.info('no need to send')
    this.nextName = 'updateFile'
  }
}
